

namespace Nutils
{
    namespace Shelf
    {
        public class File
        {
            public string sFileNameInDepo;
            public string sRev;
            public string sAction;
            public string sDepoPort;
            public string sDocBody;
            private System.Text.Encoding _encoding;
            public System.Text.Encoding encoding
            {
                get
                {
                    return _encoding;
                }
                set
                {
                    _encoding = value;
                    if (_encoding == System.Text.Encoding.UTF8)
                    {
                        _encoding = System.Text.Encoding.ASCII;
                    }
                }
            }

            public File()
            {
                sFileNameInDepo = "";
                sRev = "";
                sAction = "";
                sDepoPort = "";
                sDocBody = "";
                encoding = System.Text.Encoding.ASCII;
            }

            public File(Nutils.P4.CLFile cFile)
            {
                sFileNameInDepo = cFile.DepoFile;
                sRev = cFile.HaveRev;
                sAction = cFile.Action;
                sDepoPort = cFile.DepoHost;
                sDocBody = "";
                encoding = System.Text.Encoding.ASCII;

                if (sAction != "delete")
                {
                    System.IO.StreamReader hDoc = new System.IO.StreamReader(cFile.ClientFile);
                    sDocBody = hDoc.ReadToEnd();

                    encoding = Nutils.FileTypeMap.Instance().Find(cFile.ClientFile, hDoc.CurrentEncoding);

                    hDoc.Close();
                }
            }

            public void Pack(System.Xml.XmlWriter cWriter)
            {
                cWriter.WriteElementString("FileNameInDepo", sFileNameInDepo);
                cWriter.WriteElementString("RevisionNumber", sRev);
                cWriter.WriteElementString("Action", sAction);
                cWriter.WriteElementString("DepoPort", sDepoPort);
                cWriter.WriteElementString("DocBody", sDocBody);
                cWriter.WriteElementString("FileEncoding", encoding.BodyName);
            }

            public void Parse(XML.Reader cReader)
            {
                if (cReader.LocalName.Equals("FileEncoding"))
                {
                    encoding = System.Text.Encoding.GetEncoding(cReader.ReadString());
                    return;
                }
                if (cReader.LocalName.Equals("FileNameInDepo"))
                {
                    sFileNameInDepo = cReader.ReadString();
                    return;
                }

                if (cReader.LocalName.Equals("RevisionNumber"))
                {
                    sRev = cReader.ReadString();
                    return;
                }

                if (cReader.LocalName.Equals("Action"))
                {
                    sAction = cReader.ReadString();
                    return;
                }

                if (cReader.LocalName.Equals("DepoPort"))
                {
                    sDepoPort = cReader.ReadString();
                    return;
                }

                if (cReader.LocalName.Equals("DocBody"))
                {
                    sDocBody = cReader.ReadString();
                    return;
                }
            }
        }

        public class FileList : System.Collections.Generic.List<File> { };

        public class IShelf
        {
            private static string _shelfdir = @"C:\P4Shelf\";
            public static string sShelfRootDir
            {
                get
                {
                    if (!System.IO.Directory.Exists(_shelfdir))
                    {
                        System.IO.Directory.CreateDirectory(_shelfdir);
                    }
                    return _shelfdir;
                }

                set
                {
                    _shelfdir = value;
                }
            }

            public readonly string sName;
            public readonly string sPackFile;
            public string sJobName;
            public string sComment;
            public string sLastUpdated;
            public string sUpdatedBy;
            public Shelf.FileList lFiles;

            public IShelf(string sName)
            {
                this.sName = sName;
                sPackFile = sShelfRootDir + sName + ".shelf";
                lFiles = new FileList();
            }


            public static string[] FindShelves()
            {
                string[] v = System.IO.Directory.GetFiles(sShelfRootDir, "*.shelf");
                for (int i = 0; i < v.Length; ++i)
                {
                    v[i] = v[i].Replace(sShelfRootDir, "");
                    v[i] = v[i].Replace(".shelf", "");
                }

                return v;
            }


            protected void _Pack()
            {
                XML.Writer cWriter = XML.OpenWriter_WaitTillUnlocked(sPackFile);
                if (null == cWriter)
                {
                    return;
                }

                try
                {
                    cWriter.Formatting = System.Xml.Formatting.Indented;
                    cWriter.Indentation = 4;
                    cWriter.Namespaces = false;

                    cWriter.WriteStartDocument();
                    cWriter.WriteStartElement("root");

                    cWriter.WriteElementString("JobName", sJobName);
                    cWriter.WriteElementString("Comment", sComment);

                    foreach (File cShelfFile in lFiles)
                    {
                        cWriter.WriteStartElement("ShelfFile");
                        cShelfFile.Pack(cWriter);
                        cWriter.WriteEndElement();
                    }

                    cWriter.WriteEndDocument();
                }
                finally
                {
                    cWriter.Flush();
                    cWriter.Close();
                }
            }
        }


        public class Unshelver : IShelf
        {
            public Unshelver(string sName)
                : base(sName)
            {
            }

            // Reads the list of files from the database for this shelf
            // (or disk if this is a local shelf)
            public void Unpack()
            {
                lFiles = new FileList();
                Nutils.Shelf.File cFile = null;
                XML.Reader cReader = XML.OpenReader(sPackFile);
                if (null == cReader)
                {
                    return;
                }

                while (cReader.Read())
                {
                    if (cReader.Name.Equals("ShelfFile"))
                    {
                        switch (cReader.NodeType)
                        {
                            case System.Xml.XmlNodeType.EndElement:
                                lFiles.Add(cFile);
                                cFile = null;
                                break;
                            case System.Xml.XmlNodeType.Element:
                                cFile = new Shelf.File();
                                break;
                        }
                    }
                    else if (cFile == null)
                    {
                        if (cReader.LocalName.Equals("JobName"))
                        {
                            sJobName = cReader.ReadString();
                        }
                        else if (cReader.LocalName.Equals("Comment"))
                        {
                            sComment = cReader.ReadString();
                        }
                    }
                    else if (cReader.NodeType == System.Xml.XmlNodeType.Element)
                    {
                        cFile.Parse(cReader);
                    }
                }
            }

            private bool _PreInflate(Nutils.P4.MultiDepo cMultiDepo)
            {
                //       
                foreach (Shelf.File cShelvedFile in lFiles)
                {
                    Nutils.P4.Depo cDepo = cMultiDepo.FindDepo(cShelvedFile.sDepoPort);
                    if (null == cDepo)
                    {
                        Nutils.Console.Error("Failed to find a depo for: " + cShelvedFile.sFileNameInDepo);
                        return false;
                    }

                    // 
                    string sPath = cDepo.GetFileLocalPath(cShelvedFile.sFileNameInDepo);
                    if (sPath.Length < 1)
                    {
                        Nutils.Console.Error("Failed to find correct mapping for: " + cShelvedFile.sFileNameInDepo);
                        return false;
                    }

                    //
                    if (cDepo.IsFileCheckedOut(cShelvedFile.sFileNameInDepo))
                    {
                        Nutils.Console.Error("File '" + cShelvedFile.sFileNameInDepo + "' is already checked out.");
                        return false;
                    }
                }
                return true;
            }

            // 
            public void Inflate(Nutils.P4.MultiDepo cMultiDepo, bool bDoAutoResolve)
            {
                try
                {
                    if (!_PreInflate(cMultiDepo))
                    {
                        Nutils.Console.Error("Failed to inflate");
                        return;
                    }
                }
                catch (System.Exception ex)
                {
                    Nutils.Console.Error("Failed to inflate, '" + ex.Message + "'.");
                    return;
                }

                foreach (Shelf.File cShelvedFile in lFiles)
                {
                    Nutils.P4.Depo cDepo = cMultiDepo.FindDepo(cShelvedFile.sDepoPort);

                    // create a temp file filled with cShelvedFile.sDocBody.
                    string sClientFile = cDepo.GetFileLocalPath(cShelvedFile.sFileNameInDepo);
                    if (cShelvedFile.sAction.Equals("edit"))
                    {
                        string sPreInflateHaveRev = cDepo.GetCurrentFileRevision(cShelvedFile.sFileNameInDepo);
                        cDepo.SyncFileToRev(cShelvedFile.sFileNameInDepo, cShelvedFile.sRev);
                        cDepo.Checkout(cShelvedFile.sFileNameInDepo);

                        System.IO.TextWriter hFile = new System.IO.StreamWriter(sClientFile, false, cShelvedFile.encoding);
                        hFile.Write(cShelvedFile.sDocBody);
                        hFile.Close();
                        hFile = null;

                        if (bDoAutoResolve && 
                            sPreInflateHaveRev.Length > 0 &&
                            sPreInflateHaveRev != cShelvedFile.sRev)
                        {
                            cDepo.SyncFileToRev(cShelvedFile.sFileNameInDepo, sPreInflateHaveRev);
                            cDepo.AutoResolve(cShelvedFile.sFileNameInDepo);
                        }

                    }

                    else if (cShelvedFile.sAction.Equals("add"))
                    {
                        System.IO.TextWriter hFile = new System.IO.StreamWriter(sClientFile, false, cShelvedFile.encoding);
                        hFile.Write(cShelvedFile.sDocBody);
                        hFile.Close();

                        cDepo.Add(sClientFile);
                    }

                    else if (cShelvedFile.sAction.Equals("delete"))
                    {
                        cDepo.SyncFileToRev(cShelvedFile.sFileNameInDepo, cShelvedFile.sRev); ;
                        cDepo.Delete(sClientFile);
                    }

                    else
                    {
                        throw new Nutils.Exception("Unknown action: " + cShelvedFile.sAction);
                    }
                }
            }

            public void Update()
            {
                this._Pack();
            }

            public void Delete()
            {
                Hack.CheckFileLock(this.sPackFile);
                System.IO.File.Delete(this.sPackFile);
            }
        }

        public class Shelver : IShelf
        {
            private Nutils.P4.CLFileList _CLFileList;

            public Shelver(string sName)
                : base(sName)
            {
                _CLFileList = new Nutils.P4.CLFileList();
            }

            public void AddFile(Nutils.P4.CLFile cFile)
            {
                Nutils.Shelf.File cShelfFile = new Nutils.Shelf.File(cFile);
                this.lFiles.Add(cShelfFile);
                _CLFileList.Add(cFile);
            }

            // Copies files to the database (or disk if its a local shelf).
            public void Pack()
            {
                this._Pack();
            }

            public void Release()
            {
                foreach (Nutils.P4.CLFile cFile in _CLFileList)
                {
                    cFile.Revert();
                }
            }
        }

    }
}